#include <p18cxxx.h>#include "define.h"#include <delays.h>#include <stdlib.h>#include <string.h>#include <ctype.h>externchar recvbuf[];externchar* recvbuf_iPtr;externchar* recvbuf_ePtr;externconst rom unsignedchar _intro[];externconst rom unsignedchar _ul[];externconst rom unsignedchar _ur[];externconst rom unsignedchar _fy[];externconst rom unsignedchar _wh[];externconst rom unsignedchar _ct[];externconst rom unsignedchar _te[];externconst rom unsignedchar _ce[];externconst rom unsignedchar _rw[];externconst rom unsignedchar _ss[];externconst rom unsignedchar _sr[];externconst rom unsignedchar _cs[];externconst rom unsignedchar _outro[];externconst rom unsignedchar _fs[];externconst rom unsignedchar _raw1[];externconst rom unsignedchar _currwh[];externconst rom unsignedchar _dev1[];externconst rom unsignedchar _dev2[];externconst rom unsignedchar _gs[];externconst rom unsignedchar _os[];externconst rom unsignedchar _11[];externconst rom unsignedchar _12[];externconst rom unsignedchar _13[];externconst rom unsignedchar _14[];externconst rom unsignedchar _key[];externconst rom unsignedchar _enter[];externconst rom unsignedchar _ts[];externconst rom unsignedchar _cr[];externconst rom unsignedchar _lf[];externconst rom unsignedchar _count1[];externconst rom unsignedchar _count1b[];externconst rom unsignedchar _count2[];externconst rom unsignedchar _count3[];externconst rom unsignedchar _count4[];externconst rom unsignedchar _count5[];externconst rom unsignedchar _calib1[];externconst rom unsignedchar _calib2[];externconst rom unsignedchar _calib3[];externconst rom unsignedchar _calib4[];externconst rom unsignedchar _colon[];externconst rom unsignedchar _LCDmenu[];externconst rom unsignedchar _LCDcurrwh[];externconst rom unsignedchar _LCDgs[];externconst rom unsignedchar _LCDos[];externconst rom unsignedchar _LCDcount1[];externconst rom unsignedchar _LCDcount2[];externconst rom unsignedchar _LCDcount3[];externconst rom unsignedchar _LCDcount4[];externconst rom unsignedchar _LCDback[];externconst rom unsignedchar _speakCount4[];externconst rom unsignedchar _fac1[];externunsignedchar unit;/** Current mass unit, GRAMS or OUNCES. */unsignedchar mode = LOCAL;/** Current mode: LOCAL, REMOTE, or FACTORY. */unsignedchar modeOld =' ';/** Stores the previous mode. */unsignedchar func = WEIGH;/** Current function: WEIGH, COUNT, CALIBRATE, etc. */unsignedchar funcOld =' ';/** Stores the previous function. */unsignedchar phase =0;/** The phase is a subsection of func, used for stepping through COUNT and CALIBRATE. */externint mass;/** The current mass, in a GRAMS or OUNCES as specified by unit. */externint varMass;/** Current variance mass. */externint tareMass;/** Current tare mass. */externint rawMass;/** Current raw mass. */int lastMass =0x7FFF;/** Stores the previous mass. Initialised value used to assure a change upon first reading. */unsignedchar msg[10]="";/** A temporary variable for storing the mass as a string, for output. */unsignedchar speak_str[35]="";/** A temporary variable for storing the output for the TTS. */unsignedchar ok =0;/** If the user hits enter or presses okay, this is set. */unsignedchar back =0;/** If the user hits escape or presses back, this is set. */unsignedchar absMode = LOCAL;/** This is an absolute storage of whether we are in LOCAL or REMOTE (and only these options) */int countSet1Mass =0;/** Temporary variable for storing first count mass value. */int countSet2Mass =0;/** Temporary variable for storing second count mass value. */int countItems =0;/** Temporary variable for storing count item number. */int countItems2 =0;/** Temporary variable for storing count item number. */float countMassPerItem =0;/** Temporary variable for storing count mass per item. Float for accuracy. */unsignedchar stringItems[6];/** Temporary variable for storing the string of the number of items in count. */unsignedchar numInput = FALSE;/** If TRUE, numerical input are treated as such, rather than being menu options. */unsignedchar numbuf[5];/** Stores up to 4 digits of numerical input, plus space for termination character. */unsignedchar numbufi =0;/** Index for numbuf. */unsignedchar mute =0;/** Stores if the user has muted/unmuted the system. */void clearScreen(void);unsignedchar parseMode(unsignedchar iChar);unsignedchar parseFunc(unsignedchar iChar);void printInstructions(char mode,char func);void generateLine1(char key,char*function);void generateLine2(char key,char*function);externunsignedchar* get_rom_string(const rom unsignedchar* txPtr);externvoid transmit(unsignedchar* message);void redrawStatus(void);void phaseStates(void);externvoid setTare(void);externvoid int_to_string(int rawNumber,unsignedchar* string);void drawMenuLCD(void);externvoid string_LCD(unsignedchar* pointer);externvoid get_weight_line(int weight,char* unit,char* output);externvoid tts_send_string(char* command,char* output);externvoid speak(char* outstring);externvoid tts_reset(void);externchar tts_is_ready(void);externvoid calibrate(unsignedint zeroVoltage,unsignedint secondVoltage,int secondMass,char massUnit);externunsignedint getVoltage(void);void calibrate_mode(void);void count_mode(void);/**
* Checks input characters from the recieve buffer, and modifies the state of the program based
* on this input. The phaseStates function is then called, evaluating the implications of any
* changes.
*
* Important globals modified: func, mode, phase
* @pre The LCD, serial, and TTS are all initialized
* @post LCD, serial, and/or TTS output. The tare may be modified.
*/void checkState (void){unsignedchar rcb =*recvbuf_ePtr;
Delay10KTCYx(1);//Delay to fix buffer problemsif((recvbuf_ePtr != recvbuf_iPtr))//If there's a new character in the receive buffer{
rcb =*recvbuf_ePtr;//Get the character// Keypad handling - convert into char (as if serial input) to be handled by rest of routineif((rcb >='0')&&(rcb <='9'))//This is a number input from the keypad{if(numInput)//If we treat it as numerical input{
numbuf[numbufi]= rcb;
numbufi++;if(numbufi ==3){
ok =1;//maximum 4 digits in number}
rcb =0;}else//Keypad input as menu selection{switch(rcb){case'1':
rcb = WEIGH;break;case'2':
rcb = COUNT;break;case'3':
rcb = TARE;break;case'4':
rcb = CHANGEUNIT;break;case'7':
rcb = LOCAL;break;case'8':
mute =1- mute;break;case'9'://Respeak
mute =0;if(func == WEIGH)//If number, recalculate mass stuff{
int_to_string(mass, msg);
string_LCD(get_rom_string(_LCDcurrwh));
string_LCD(msg);strcpy(speak_str,msg);if(unit == GRAMS){
strcatpgm2ram(speak_str,_gs);}else{
strcatpgm2ram(speak_str,_os);}}if(tts_is_ready()){
speak(speak_str);}
rcb =0;break;}}}// Reset flags
ok =0;
back =0;//Need to detect special characters and input number, but I'm not sure how to store it all yet.//A small buffer is needed for the numbers.if(rcb =='\r'|| rcb =='#')//If carriage return or OK{
rcb = OK;
ok =1;}if(rcb == BACK || rcb =='\b'|| rcb ==27)//If back, backspace or escape{
rcb = BACK;
back =1;}
mode = parseMode(rcb);// Check for a valid mode character.if(!mode){
mode = modeOld;}
func = parseFunc(rcb);// Check for a valid function character.if(!func){
func = funcOld;}if((mode != FACTORY)&&(func >=65)&&(func <=90))// Factory functions allowed only in factory mode.{//Between 65 and 90 are capital letters, so factory function characters must be capitals
func = funcOld;if((func >=65)&&(func <=90)){
func = WEIGH;}}if(mode != modeOld || func != funcOld)//If the state has changed{
phase =0;//Reset the phase
lastMass =0x7FFF;}if(rcb == CHANGEUNIT){
unit =!unit;}
rcb =0;
recvbuf_ePtr++;if(recvbuf_ePtr >= recvbuf + I_BUFSIZE){
recvbuf_ePtr = recvbuf;}}//Set absolute mode settingif(mode != modeOld){if(mode == REMOTE || mode == FACTORY){
absMode = REMOTE;
func = WEIGH;}if(mode == LOCAL){
absMode = LOCAL;
func = MENU;}
redrawStatus();}
phaseStates();//Run through main logic
modeOld = mode;
funcOld = func;}/**
* Calls the relevant functions to clear the screen on the terminal and redraw the menu.
* @pre The serial must be initialised
* @post See clearScreen and printInstructions in Scales.c
*/void redrawStatus(void){if(absMode == LOCAL)//Since this function is for serial only, quit if we're not using serial{return;}
clearScreen();
printInstructions(mode, func);}/**
* Evaluates the effect of the func and mode variables, which may have been modified in the checkState
* function.
*
* This method will output menus and displays to the serial, LCD, and TTS where appropriate, based on
* the current state of the program.
*
* The phase variable is used extensively in the functions Count and Calibrate to step through the
* individual states of the functions. This method will, based in received input, choose when to
* move forwards, and will, when complete, return to the main menu or weigh function, where appropriate.
*
* Important globals modified: func, mode, phase
* @pre LCD, serial, and TTS are initialised
* @post LCD, serial, and/or TTS output where appropriate. Calibration and Tare may be performed.
*/void phaseStates(void){if(back){if(mode == LOCAL){
func = MENU;
phase =0;}else{
func = WEIGH;
phase =0;}
back =0;}switch(func){case MENU://Should be LCD onlyif(func != funcOld){
drawMenuLCD();}break;case WEIGH:if(mass != lastMass){
int_to_string(mass, msg);if(absMode == REMOTE){
redrawStatus();
transmit(get_rom_string(_currwh));
transmit(msg);if(unit == GRAMS){
transmit(get_rom_string(_gs));}else{
transmit(get_rom_string(_os));}
transmit(get_rom_string(_outro));}else{
string_LCD(get_rom_string(_LCDcurrwh));
string_LCD(msg);strcpy(speak_str,msg);if(unit == GRAMS){
string_LCD(get_rom_string(_LCDgs));
strcatpgm2ram(speak_str,_gs);}else{
string_LCD(get_rom_string(_LCDos));
strcatpgm2ram(speak_str,_os);}
string_LCD(get_rom_string(_LCDback));if(tts_is_ready()&&!mute){
speak(speak_str);}}
lastMass = mass;}break;case COUNT:void count_mode(void);break;case TARE:
setTare();
func = WEIGH;
phase =0;break;case CALIBRATE://Should never happen in local mode, so not designed to cope with LCD
calibrate_mode(/*mode, func, phase, ok, unit*/);break;case STATS:if(mass != lastMass){
int_to_string(varMass, msg);if(absMode == REMOTE){
redrawStatus();
transmit(get_rom_string(_dev2));
transmit(get_rom_string(_outro));
transmit(get_rom_string(_dev1));
transmit(msg);if(unit == GRAMS){
transmit(get_rom_string(_gs));}else{
transmit(get_rom_string(_os));}
transmit(get_rom_string(_outro));}
lastMass = mass;}break;case RAW:if(mass != lastMass){
rawMass = mass + tareMass;
int_to_string(rawMass, msg);if(absMode == REMOTE){
redrawStatus();
transmit(get_rom_string(_raw1));
transmit(msg);if(unit == GRAMS){
transmit(get_rom_string(_gs));}else{
transmit(get_rom_string(_os));}
transmit(get_rom_string(_outro));}
lastMass = mass;}break;case SAMPLES://Not written yetbreak;}}/**
* Draws the main menu on the LCD. This menu shows the main options available to
* the user.
* @pre LCD is initialised
* @post The menu is added to the LCD buffer
*/void drawMenuLCD(void){if(absMode == REMOTE){return;}
string_LCD(get_rom_string(_LCDmenu));if(unit == GRAMS)//Append !unit{
string_LCD(get_rom_string(_os));}else{
string_LCD(get_rom_string(_gs));}}/**
* To be called strictly by phaseStates only, under case CALIBRATE.
* Uses the phase variable to move between stages in the calibrate
* state.
*
* See calibrate in scales.c for further postcondition and global variable
* information.
*
* Important globals modified: phase, func.
* @pre func = CALIBRATE, mode = FACTORY, serial initialised
* @post Phase moves on, or calibration is performed and finishes output to serial.
*/void calibrate_mode(void){staticunsignedint v1, v2, m1, m2;unsignedchar calibrationString[16];switch(phase){case0://First prompt: Unload scales and press enter
redrawStatus();
transmit(get_rom_string(_calib1));
transmit(get_rom_string(_enter));
transmit(get_rom_string(_outro));
phase =1;
ok =0;break;case1://Wait until OK and store voltageif(ok){
v1 = getVoltage();
m1 =0;
ok =0;
phase =2;}break;case2://Second prompt//Ask user to put some known quantity on the scales
redrawStatus();
transmit(get_rom_string(_calib2));
transmit(get_rom_string(_enter));
transmit(get_rom_string(_outro));
ok =0;
phase =3;case3://Wait until OK and store massif(ok){
v2 = getVoltage();
ok =0;
phase =4;}break;case4://Third prompt//Ask user to specify how much is on the scales in the working unit
redrawStatus();
transmit(get_rom_string(_calib3));if(unit == GRAMS){
transmit(get_rom_string(_gs));}else{
transmit(get_rom_string(_os));}
transmit(get_rom_string(_colon));
transmit(get_rom_string(_outro));
phase =5;case5://Wait until OK and store value
numInput = TRUE;//Allow the user to input numbersif(ok){
numbuf[numbufi]='\0';
transmit(numbuf);
m2 =atoi(numbuf);//must fix to accept user input
numInput = FALSE;
ok =0;
phase =6;}break;case6://Fourth prompt
redrawStatus();
transmit(get_rom_string(_calib4));
transmit(get_rom_string(_calib1));
transmit(get_rom_string(_enter));
transmit(get_rom_string(_outro));//sprintf(calibrationString,"v1 = %d, m1 = %d, v2 = %d, m2 = %d\n",v1,m1,v2,m2);//transmit(calibrationString);//Delay10KTCYx(10);
phase =7;break;case7://Wait for OK, then back to main menu
calibrate(v1, v2, m2, unit);// setTare();if(ok){
ok =0;
func = WEIGH;
phase =0;}break;}}/**
* To be called strictly by phaseStates only, under case COUNT.
* Uses the phase variable to move between stages in the count state.
* If count is performed, the results will be displayed.
* Output to serial, or LCD and TTS.
* Important globals modified: phase, func.
* @pre func = COUNT, and serial, TTS, and LCD initialised
* @post Phase moves on, or count is performed and finished.
*/void count_mode(void){switch(phase){case0://First prompt//Please load a number of identical items....if(absMode == REMOTE){
redrawStatus();
transmit(get_rom_string(_count1));
transmit(get_rom_string(_enter));
transmit(get_rom_string(_outro));}else{
string_LCD(get_rom_string(_LCDcount1));if(tts_is_ready()&&!mute){strcpy(speak_str,get_rom_string(_LCDcount1));
speak(speak_str);}}
phase =1;//String has been transmitted - wait for user to confirmbreak;case1://Wait until OK and store massif(ok){
countSet1Mass = mass;
ok =0;
phase =2;}break;case2://Second prompt//Ask user how many items are on the scalesif(absMode == REMOTE){
redrawStatus();
transmit(get_rom_string(_count2));
transmit(get_rom_string(_enter));
transmit(get_rom_string(_outro));}else{
string_LCD(get_rom_string(_LCDcount2));if(tts_is_ready()&&!mute){strcpy(speak_str,get_rom_string(_LCDcount2));
speak(speak_str);}}
phase =3;case3://Wait until OK and store value
numInput = TRUE;if(ok){
numbuf[numbufi]='\0';
transmit(numbuf);
countItems =atoi(numbuf);
numInput = FALSE;
numbufi =0;
ok =0;
phase =4;}break;case4://Third prompt//Ask user to load any number of items to be counted.if(absMode == REMOTE){
redrawStatus();
transmit(get_rom_string(_count3));
transmit(get_rom_string(_enter));}else{
string_LCD(get_rom_string(_LCDcount3));if(tts_is_ready()&&!mute){strcpy(speak_str,get_rom_string(_LCDcount3));
speak(speak_str);}}
phase =5;case5://Wait until OK and store massif(ok){
countSet2Mass = mass;
ok =0;
phase =6;}break;case6://Fourth prompt
countMassPerItem =1.0* countSet1Mass / countItems;
countItems2 =(int)(countSet2Mass / countMassPerItem +0.5);
int_to_string(countItems2,stringItems);if(absMode == REMOTE){
redrawStatus();
transmit(get_rom_string(_count4));
transmit(stringItems);
transmit(get_rom_string(_count5));
transmit(get_rom_string(_outro));}else{
string_LCD(get_rom_string(_LCDcount4));
string_LCD(stringItems);
string_LCD(get_rom_string(_LCDback));if(tts_is_ready()&&!mute){strcpy(speak_str,stringItems);
strcatpgm2ram(speak_str,_speakCount4);
speak(speak_str);}}
phase =7;break;case7://Wait for OK, then back to main menuif(ok){
ok =0;
func = WEIGH;
phase =0;}break;}}